package event;

import editor.PwnIDE;
import model.caret.TextCaret;
import model.code.Function;
import model.code.line.LineList;

/**
 * This is for events in which text is replaced in a {@link Function}.
 *
 * @author Neil Dickson
 */
public class TextReplacementEvent implements Undoable {
	private	LineList	lines;
	private	String		previousText;
	private	int			firstLine;
	private	int			firstLineCol;
	private	int			lastLine;
	private	int			lastLineCol;

	/**
	 * This array contains TextCaret representations of all of the references added because of a Symbol being renamed by this TextReplacementEvent.
	 *
	 * The reason for this is to fix a major bug whereby renaming a Symbol and then undoing would incorrectly update references that were not referring to the Symbol before the rename.
	 * In some cases, undoing again could cause a crash, e.g. trying to delete text that doesn't exist.  Now, these added references are removed before undoing the text change.
	 *
	 * The column number in the TextCarets actually represents the reference number that was changed from null to the Symbol, and that should be changed back to null upon undo.
	 */
	private	TextCaret[]	addedRenameReferences;

	/**
	 * Creates a new TextReplacementEvent.
	 * @param lines			the {@link LineList} containing the text replaced
	 * @param previousText	the old text
	 * @param firstLine		first line index of the new text
	 * @param firstLineCol	first column on the first line of the new text
	 * @param lastLine		last line index of the new text
	 * @param lastLineCol	last column + 1 on the last line of the new text
	 */
	public TextReplacementEvent(LineList lines, String previousText, int firstLine, int firstLineCol, int lastLine, int lastLineCol) {
		this.lines = lines;
		this.previousText = previousText;
		this.firstLine = firstLine;
		this.firstLineCol = firstLineCol;
		this.lastLine = lastLine;
		this.lastLineCol = lastLineCol;
		this.addedRenameReferences = null;
	}

	public Undoable undo() {
		// Invalidate added references first to avoid the condition described under addedRenameReferences.
		if (addedRenameReferences!=null) {
			for (TextCaret caret : addedRenameReferences) {
				caret.getLineList().get(caret.getLine()).getReferences()[caret.getCol()] = null;
			}
		}
		// NOTE: The inverse of a TextReplacementEvent should always be a TextReplacementEvent.
		TextReplacementEvent inverse = lines.replace(firstLine,firstLineCol,lastLine,lastLineCol,previousText);
		PwnIDE.ui.goToCaret(new TextCaret(null,lines,inverse.firstLine,inverse.firstLineCol,inverse.lastLine,inverse.lastLineCol,0));
		return inverse;
	}

	public String getDescription() {
		String name = (lines.getParent()!=null) ? " in "+lines.getParent().getName() : "";
		if (isInsertion()) {
			return "Inserted text"+name;
		}
		if (isDeletion()) {
			return "Deleted text"+name;
		}
		return "Replaced text"+name;
	}

	public boolean isDeletion() {
		return previousText.length()>0 && firstLine==lastLine && firstLineCol==lastLineCol;
	}
	public boolean isInsertion() {
		return previousText.length()==0;
	}

	public String getPreviousText() {
		return previousText;
	}

	public int getFirstLine() {
		return firstLine;
	}

	public int getFirstLineCol() {
		return firstLineCol;
	}

	public int getLastLine() {
		return lastLine;
	}

	public int getLastLineCol() {
		return lastLineCol;
	}

	public void addReferences(TextCaret[] addedRenameReferences) {
		if (addedRenameReferences!=null) {
			this.addedRenameReferences = addedRenameReferences;
		}
	}
}
